package com.fisk.auth.service.impl;

import com.fisk.auth.constants.JwtConstants;
import com.fisk.auth.dto.UserAuthDTO;
import com.fisk.auth.service.UserAuthService;
import com.fisk.common.core.constants.SystemConstants;
import com.fisk.common.core.response.ResultEntity;
import com.fisk.common.core.response.ResultEntityBuild;
import com.fisk.common.core.response.ResultEnum;
import com.fisk.common.core.user.UserInfo;
import com.fisk.common.framework.exception.FkException;
import com.fisk.common.framework.jwt.JwtUtils;
import com.fisk.common.framework.jwt.model.Payload;
import com.fisk.common.framework.jwt.model.UserDetail;
import com.fisk.common.framework.redis.RedisKeyBuild;
import com.fisk.common.framework.redis.RedisKeyEnum;
import com.fisk.common.framework.redis.RedisUtil;
import com.fisk.system.client.UserClient;
import com.fisk.system.dto.userinfo.UserDTO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpStatus;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import java.util.UUID;

import static com.fisk.auth.constants.JwtConstants.COOKIE_NAME;

/**
 * @author Lock
 * @date 2021/5/17 13:53
 */
@Service
@Slf4j
public class UserAuthServiceImpl implements UserAuthService {

    /**
     * Cookie中用于记录全局凭证的key
     */
    private static final String COOKIE_USER_TICKET = "cookie_user_ticket";

    @Resource
    private UserClient userClient;

    @Resource
    private JwtUtils jwtUtils;

    @Resource
    private RedisUtil redis;

    @Override
    public ResultEntity<String> login(UserAuthDTO userAuthDTO) {

        // 1.授权中心携带用户名密码，到用户中心(数据库)查询用户
        // 请求user服务获取用户信息
        UserDTO userDTO = null;

        try {
            ResultEntity<UserDTO> res = userClient.queryUser(userAuthDTO.getUserAccount(), userAuthDTO.getPassword());
            if (res.code == ResultEnum.SUCCESS.getCode()) {
                userDTO = res.data;
            } else {
                return ResultEntityBuild.build(ResultEnum.getEnum(res.code));
            }
        } catch (Exception e) {
            throw new FkException(ResultEnum.REMOTE_SERVICE_CALLFAILED);
        }

        // 创建自定义荷载对象
        UserDetail userDetail = UserDetail.of(userDTO.getId(), userDTO.getUserAccount());
        // 生成jwt: token
        String token = SystemConstants.AUTH_TOKEN_HEADER + jwtUtils.createJwt(userDetail);
        UserInfo userInfo = UserInfo.of(userDTO.getId(), userDTO.getUserAccount(), token);

        int permanentToken = 102;
        if (userInfo.id == permanentToken) {
            redis.set(RedisKeyBuild.buildLoginUserInfo(userInfo.id), userInfo, -1);
        } else {
            boolean res = redis.set(RedisKeyBuild.buildLoginUserInfo(userInfo.id), userInfo, RedisKeyEnum.AUTH_USERINFO.getValue());
        }

        return ResultEntityBuild.buildData(ResultEnum.SUCCESS, token);
    }

    @Override
    public void logout(HttpServletRequest request) {
        // 1.获取token
        String token = request.getHeader(SystemConstants.HTTP_HEADER_AUTH).replace(SystemConstants.AUTH_TOKEN_HEADER, "");
        // 2.校验token的有效性
        Payload payload = null;
        try {
            payload = jwtUtils.parseJwt(token);
        } catch (Exception e) {
            // 3.如果无效，什么都不做
            return;
        }
        UserDetail userDetail = payload.getUserDetail();
        // 4删除redis数据
        redis.del(RedisKeyBuild.buildLoginUserInfo(userDetail.getId()));
    }

    @Override
    public ResultEntity<String> getToken(UserAuthDTO dto) {
        UserDetail userDetail = UserDetail.of(dto.getTemporaryId(), dto.getUserAccount());
        // 生成jwt: token
        String token = SystemConstants.AUTH_TOKEN_HEADER + jwtUtils.createJwt(userDetail);
        UserInfo userInfo = UserInfo.of(dto.getTemporaryId(), dto.getUserAccount(), token);
        // 一个小时有效期
        redis.set(RedisKeyBuild.buildLoginUserInfo(userInfo.id), userInfo, RedisKeyEnum.AUTH_USERINFO.getValue());
        return ResultEntityBuild.buildData(ResultEnum.SUCCESS, token);
    }

    private void writeCookie(HttpServletResponse response, String token) {
        // cookie的作用域
        Cookie cookie = new Cookie(COOKIE_NAME, token);
        cookie.setDomain(JwtConstants.DOMAIN);
        // 是否禁止JS操作cookie，避免XSS攻击
        cookie.setHttpOnly(true);
        // cookie有效期，-1就是跟随当前会话，浏览器关闭就消失
        cookie.setMaxAge(-1);
        // cookie作用的路径，/代表一切路径
        cookie.setPath("/");
        response.addCookie(cookie);
    }

    @Override
    public ResultEntity<Object> verifyToken(String tmpTicket, HttpServletRequest request, HttpServletResponse response) {
        // 获取redis中tmpTicket临时凭证信息
        String tmpTicketValue = (String) redis.get(RedisKeyBuild.buildSsoUserTicket(tmpTicket));
        if (StringUtils.isEmpty(tmpTicketValue)){
            log.info("用户临时凭证不存在");
            return ResultEntityBuild.build(ResultEnum.AUTH_LOGIN_INFO_INVALID);
        }

        // 临时凭证合法，则销毁，并获取全局token
        if (!tmpTicketValue.equals(tmpTicket)){
            log.info("用户临时凭证非法");
            return ResultEntityBuild.build(ResultEnum.AUTH_LOGIN_INFO_INVALID);
        }
        // 销毁临时凭证
        redis.del(RedisKeyBuild.buildSsoUserTicket(tmpTicket));

        // 验证并获取用户的userTicket
        String userTicket = getCookie(request, COOKIE_USER_TICKET);
        Long userId = (Long) redis.get(RedisKeyBuild.buildSsoUserTicket(userTicket));
        if (userId == null){
            log.info("用户凭证非法");
            return ResultEntityBuild.build(ResultEnum.AUTH_LOGIN_INFO_INVALID);
        }

        // 判断redis中用户会话是否存在
        UserInfo userInfo = (UserInfo) redis.get(RedisKeyBuild.buildSsoUserTokenInfo(userId));
        if (userInfo == null){
            log.info("用户信息不存在");
            return ResultEntityBuild.build(ResultEnum.AUTH_LOGIN_INFO_INVALID);
        }

        return ResultEntityBuild.build(ResultEnum.SUCCESS, userInfo);
    }

    @Override
    public ResultEntity<Object> ssoLogin(String username, String password, HttpServletRequest request, HttpServletResponse response) {
        // 校验账号信息
        if (StringUtils.isEmpty(username) || StringUtils.isEmpty(password)){
            log.error("用户账号有误,账号-[{}],密码[{}]", username, password);
            return ResultEntityBuild.build(ResultEnum.USER_ACCOUNTPASSWORD_ERROR);
        }

        // 查询用户信息
        UserDTO userDTO = null;
        try{
            ResultEntity<UserDTO> userResult = userClient.queryUser(username, password);
            if (userResult.code != HttpStatus.SC_OK){
                return ResultEntityBuild.build(ResultEnum.getEnum(userResult.code));
            }
            userDTO = userResult.data;
        }catch (Exception e){
            throw new FkException(ResultEnum.REMOTE_SERVICE_CALLFAILED);
        }

        // 创建用户会话并加入redis
        UserDetail userDetail = UserDetail.of(userDTO.getId(), userDTO.getUserAccount());
        String uniqueToken = SystemConstants.AUTH_TOKEN_HEADER + jwtUtils.createJwt(userDetail);
        UserInfo userInfo = UserInfo.of(userDTO.getId(), userDTO.getUserAccount(), uniqueToken);
        redis.set(RedisKeyBuild.buildSsoUserTokenInfo(userInfo.getId()), userInfo, 30 * 60);

        // 创建全局凭证、并写入cookie、关联userId
        String userTicket = UUID.randomUUID().toString().trim();
        setCookie(COOKIE_USER_TICKET, userTicket, response);
        redis.set(RedisKeyBuild.buildSsoUserTicket(userTicket), userInfo.getId());

        // 创建临时凭证
        String tmpTicket = UUID.randomUUID().toString().trim();
        redis.set(RedisKeyBuild.buildSsoTmpTicket(tmpTicket), tmpTicket, 31 * 60);

        return ResultEntityBuild.build(ResultEnum.SUCCESS, tmpTicket);
    }


    private String getCookie(HttpServletRequest request, String key){
        Cookie[] cookieList = request.getCookies();
        if (cookieList == null || StringUtils.isEmpty(key)){
            return null;
        }
        String value = null;
        for(int i = 0; i < cookieList.length; i++){
            if (cookieList[i].getName().equals(key)){
                value = cookieList[i].getValue();
                break;
            }
        }
        return value;
    }

    private void setCookie(String key, String val,  HttpServletResponse response){
        Cookie cookie = new Cookie(key, val);
        cookie.setDomain(JwtConstants.DOMAIN);
        cookie.setHttpOnly(true);
        cookie.setMaxAge(-1);
        cookie.setPath("/");
        response.addCookie(cookie);
    }

    private void delCookie(String key, HttpServletResponse response){
        Cookie cookie = new Cookie(key, null);
        cookie.setDomain(JwtConstants.DOMAIN);
        cookie.setHttpOnly(true);
        cookie.setPath("/");
        cookie.setMaxAge(-1);
        response.addCookie(cookie);
    }

    @Override
    public ResultEntity<Object> ssoLoginOut(Long userId, HttpServletRequest request, HttpServletResponse response) {
        // 获取全局凭证
        String userTicket = getCookie(request, COOKIE_USER_TICKET);

        // 清空redis、cookie中的全局凭证
        delCookie(COOKIE_USER_TICKET, response);
        redis.del(RedisKeyBuild.buildSsoUserTicket(userTicket));

        // 清空用户会话
        redis.del(RedisKeyBuild.buildSsoUserTokenInfo(userId));
        return ResultEntityBuild.build(ResultEnum.SUCCESS);
    }

    @Override
    public ResultEntity<Object> refreshToken(Long userId, HttpServletRequest request, HttpServletResponse response) {
        redis.expire(RedisKeyBuild.buildSsoUserTokenInfo(userId), 30 * 60);
        return ResultEntityBuild.build(ResultEnum.SUCCESS);
    }
}
